/**
检测并筛选出指定格式(以外)的文件

调用方式：

4个参数，最后一个可以省略

utfCheck(目标文件夹, 源文件夹, 需要检测的文件的扩展名，筛选时忽略的文件格式);
其中扩展名可以写多个，用逗号分隔，"*"代表所有文件类型
最后那个文件格式是要忽略的，也就是说除了这里指定的文件格式以外都筛选出来，省略的话就是UTF-16LE

例:

@loadplugin module=UTFCheck.dll
@iscript
utfCheck("C:\\dstdic", "C:\\srcdic", "ks,tjs,asd", "UTF-16LE, UTF-8");
@endscript




*/

//---------------------------------------------------------------------------
#include <windows.h>
#include "tp_stub.h"
#include <string>

//---------------------------------------------------------------------------

bool checkFileEncoding(const std::wstring& filename, const std::wstring& extensionList, const std::wstring& exceptTypeList)
{
    size_t dotpos = filename.find_last_of(TEXT('.'));
    if(dotpos != std::wstring::npos && dotpos + 1 < filename.length() && extensionList.find(filename.substr(dotpos + 1)) != std::wstring::npos)
    {
        HANDLE hFile;
        DWORD dwByte;

        hFile = CreateFile(filename.c_str(), GENERIC_READ, 0, NULL,
				OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
        if(hFile == INVALID_HANDLE_VALUE)
        {
            return false;
        }

        byte bom[3];
        if(!ReadFile(hFile, bom, 3, &dwByte, NULL))
        {
            CloseHandle(hFile);
            return false;
        }
        CloseHandle(hFile);

        if(bom[0] != 0xFF || bom[1] != 0xFE)
            return true;
    }
    return false;
}

std::wstring utfCheck(const std::wstring& dstdic, const std::wstring& srcdic, const std::wstring& extensionList, const std::wstring& exceptTypeList)
{
	if(!CreateDirectory(dstdic.c_str(), NULL))
		if(GetLastError() == ERROR_PATH_NOT_FOUND)
			return TJS_W("");

	WIN32_FIND_DATA findFileData;
	memset(&findFileData, 0, sizeof(WIN32_FIND_DATA));

    std::wstring dstdicTemp = dstdic;
    std::wstring srcdicTemp = srcdic;
	if(dstdicTemp.at(dstdicTemp.length() - 1) != TJS_W('\\'))
		dstdicTemp += TJS_W("\\");
	if(srcdicTemp.at(srcdicTemp.length() - 1) != TJS_W('\\'))
		srcdicTemp += TJS_W("\\");
    std::wstring copyedFile = TJS_W("");

	HANDLE hFindFile = FindFirstFile((srcdicTemp + TJS_W("*")).c_str(), &findFileData);
	if(0 != lstrcmp(findFileData.cFileName, TJS_W(".")) && 0 == lstrcmp(findFileData.cFileName, TJS_W("..")))
	{
		if(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY != 0)//文件夹
		{
			copyedFile += utfCheck(dstdicTemp + findFileData.cFileName, srcdicTemp + findFileData.cFileName, extensionList, exceptTypeList);
		}
		else//文件
		{
		    if(checkFileEncoding(srcdicTemp + findFileData.cFileName, extensionList, exceptTypeList))
		    {
                CopyFile((srcdicTemp + findFileData.cFileName).c_str(), (dstdicTemp + findFileData.cFileName).c_str(), false);
                copyedFile += dstdicTemp + findFileData.cFileName;
            }
		}
	}
	while(FindNextFile(hFindFile, &findFileData))
	{
		if(0 == lstrcmp(findFileData.cFileName, TJS_W(".")))
			continue;
		if(0 == lstrcmp(findFileData.cFileName, TJS_W("..")))
			continue;
		if((findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)//文件夹
		{
			copyedFile += utfCheck(dstdicTemp + findFileData.cFileName, srcdicTemp + findFileData.cFileName, extensionList, exceptTypeList);
		}
		else//文件
		{
		    if(checkFileEncoding(srcdicTemp + findFileData.cFileName, extensionList, exceptTypeList))
		    {
                CopyFile((srcdicTemp + findFileData.cFileName).c_str(), (dstdicTemp + findFileData.cFileName).c_str(), false);
                copyedFile += dstdicTemp + findFileData.cFileName;
            }
		}
	}

    return copyedFile;
}



//---------------------------------------------------------------------------
// 检测并筛选出指定格式(以外)的文件
//---------------------------------------------------------------------------
class tTJSExtractTagsFromKSFunction : public tTJSDispatch
{
	tjs_error TJS_INTF_METHOD FuncCall(
		tjs_uint32 flag, const tjs_char * membername, tjs_uint32 *hint,
		tTJSVariant *result,
		tjs_int numparams, tTJSVariant **param, iTJSDispatch2 *objthis);
} * TJSExtractTagsFromKSFunction;

tjs_error TJS_INTF_METHOD tTJSExtractTagsFromKSFunction::FuncCall(
	tjs_uint32 flag, const tjs_char * membername, tjs_uint32 *hint,
	tTJSVariant *result,
	tjs_int numparams, tTJSVariant **param, iTJSDispatch2 *objthis)
{
	if(numparams < 3) return TJS_E_BADPARAMCOUNT;

	tTJSString dstdic = *param[0];
	tTJSString srcdic = *param[1];
	tTJSString extensionList = *param[2];
    tTJSString exceptTypeList = TJS_W("UTF-16LE");
    if(numparams >= 3)
        exceptTypeList = *param[3];

	ttstr copyedFile = utfCheck(dstdic.c_str(), srcdic.c_str(), extensionList.c_str(), exceptTypeList.c_str()).c_str();

	if(result)
	{
		*result = copyedFile;
	}

	return TJS_S_OK;
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason,
	void* lpReserved)
{
	return 1;
}
//---------------------------------------------------------------------------
static tjs_int GlobalRefCountAtInit = 0;
extern "C" HRESULT _stdcall _export V2Link(iTVPFunctionExporter *exporter)
{
	// スタブの初期化(必ず記述する)
	TVPInitImportStub(exporter);

	// AverageFunction の作成と登録
	tTJSVariant val;

	// TJS のグローバルオブジェクトを取得する
	iTJSDispatch2 * global = TVPGetScriptDispatch();



	//-----------------------------------------------------------------------
	// 1 まずオブジェクトを作成
	TJSExtractTagsFromKSFunction = new tTJSExtractTagsFromKSFunction();

	// 2 AverageFunction を tTJSVariant 型に変換
	val = tTJSVariant(TJSExtractTagsFromKSFunction);

	// 3 すでに val が AverageFunction を保持しているので、AverageFunction は
	//   Release する
	TJSExtractTagsFromKSFunction->Release();


	// 4 global の PropSet メソッドを用い、オブジェクトを登録する
	global->PropSet(
		TJS_MEMBERENSURE, // メンバがなかった場合には作成するようにするフラグ
		TJS_W("utfCheck"), // メンバ名 ( かならず TJS_W( ) で囲む )
		NULL, // ヒント ( 本来はメンバ名のハッシュ値だが、NULL でもよい )
		&val, // 登録する値
		global // コンテキスト ( global でよい )
		);
	//-----------------------------------------------------------------------

	// - global を Release する
	global->Release();

	// もし、登録する関数が複数ある場合は 1 ～ 4 を繰り返す


	// val をクリアする。
	// これは必ず行う。そうしないと val が保持しているオブジェクト
	// が Release されず、次に使う TVPPluginGlobalRefCount が正確にならない。
	val.Clear();


	// この時点での TVPPluginGlobalRefCount の値を
	GlobalRefCountAtInit = TVPPluginGlobalRefCount;
	// として控えておく。TVPPluginGlobalRefCount はこのプラグイン内で
	// 管理されている tTJSDispatch 派生オブジェクトの参照カウンタの総計で、
	// 解放時にはこれと同じか、これよりも少なくなってないとならない。
	// そうなってなければ、どこか別のところで関数などが参照されていて、
	// プラグインは解放できないと言うことになる。

	return S_OK;
}
//---------------------------------------------------------------------------
extern "C" HRESULT _stdcall _export V2Unlink()
{
	// 吉里吉里側から、プラグインを解放しようとするときに呼ばれる関数。

	// もし何らかの条件でプラグインを解放できない場合は
	// この時点で E_FAIL を返すようにする。
	// ここでは、TVPPluginGlobalRefCount が GlobalRefCountAtInit よりも
	// 大きくなっていれば失敗ということにする。
	if(TVPPluginGlobalRefCount > GlobalRefCountAtInit) return E_FAIL;
		// E_FAIL が帰ると、Plugins.unlink メソッドは偽を返す

	// TJS のグローバルオブジェクトに登録した average 関数などを削除する

	// - まず、TJS のグローバルオブジェクトを取得する
	iTJSDispatch2 * global = TVPGetScriptDispatch();

	// - global の DeleteMember メソッドを用い、オブジェクトを削除する
	if(global)
	{
		// TJS 自体が既に解放されていたときなどは
		// global は NULL になり得るので global が NULL でない
		// ことをチェックする

		global->DeleteMember(
			0, // フラグ ( 0 でよい )
			TJS_W("utfCheck"), // メンバ名
			NULL, // ヒント
			global // コンテキスト
			);
			// 登録した関数が複数ある場合は これを繰り返す
	}

	// - global を Release する
	if(global) global->Release();

	// スタブの使用終了(必ず記述する)
	TVPUninitImportStub();

	return S_OK;
}
//---------------------------------------------------------------------------
static tjs_char option_message[] =
L"テストカテゴリ:テスト設定項目1;これはテスト用のオプション設定項目です。|"
L"testoption1|select,*default;デフォルトの値,1;選択可能な値1,2;選択可能な値2\n"
L"テストカテゴリ:テスト設定項目2;これはテスト用のオプション設定項目です。|"
L"testoption2|string(64),*デフォルトの文字列\n";
//---------------------------------------------------------------------------
extern "C" const wchar_t* _stdcall _export GetOptionDesc()
{
	// GetOptionDesc は、吉里吉里設定ツールに渡すオプションの情報を返すための
	// 関数。
	// 一行につき一つのオプションに関する情報を書く。
	// 書式は
	// カテゴリ:設定名;説明|オプション名|オプションとして設定可能な値
	// (区切り記号に注意)
	// 「オプション名」には '-' (ハイフン) はつけない。
	// 「オプションとして設定可能な値」は以下の書式である。
	// タイプ,値の値;簡単な説明[,値;値の簡単な説明 ...]
	// 「タイプ」は現バージョンでは select と string のみ使用可能。

	// 「タイプ」に select を指定すると、コンボボックスから値を選択することが
	// できるようになる。
	// 「値;値の簡単な説明」の先頭に * をつけるとそれがデフォルトのオプション
	// であることを示す。デフォルトのオプションは必ず一つは無くてはならない。
	// 「値」は実際に吉里吉里本体へのオプションとして渡されるものを、
	// 「値の簡単な説明」は人間が読んで理解できるものを書く。

	// 「タイプ」に string を指定すると、エディットボックスでユーザが任意の文
	// 字列を入力できるようになる。
	// 「タイプ」に string を指定する場合は、string(n),*デフォルト文字列 の
	// 形で n には最大文字数を記述する。0 で文字数制限はなくなるが、できれば
	// 制限をつけたほうがよい。
	// 「デフォルト文字列」にはデフォルトの文字列を記述する。string(n) のあと
	// の , (カンマ) も * (アステリスク) も省略できない。

	// 例は /msg/win32/option_desc_ja.txt にもある。


	return option_message;

	// 各オプション末や、最後の改行記号を忘れると大変なことになる。


	// 吉里吉里設定などは、そのプラグインに設定可能な項目があるかどうかを
	// プラグインDLLの
	// 先頭2KB以内に "(--has-option--)" という文字列があるかどうか
	// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	// (２重引用符は含めず)
	// で判断するため、先頭2KB以内にこれを書かなければならない。
	// bcc の場合はリンカオプション /GC で /GC"(--has-option--)"
	// と記述し、VC++ の場合もリンカオプション /COMMENT で
	// /COMMENT:"(--has-option--)" と記述する。
	// BCB の IDE からは プロジェクトオプションの「リンカ(詳細)」の
	// 「イメージのコメント」に "(--has-option--)" と書く。
	// (２重引用符で囲まないとエラーになるかもしれない)

	// UPX などで圧縮するとここの部分が消されてしまうので、
	// ヘッダの未使用領域 (0x80～0x1ff の 00 で埋まっている所など) に
	// 無理矢理書くなどする。

	// 本体に渡されたオプションは TVPGetCommandLine 関数で取得できる。
	// tTJSVariant val;
	// if(TVPGetCommandLine(TJS_W("-testoption1"), &val))
	// {
	//      // ここに オプション "-testoption1" が渡されていたときの
	//      // 処理を書く。val にはその値が入っている。
	//      // /environ/win32/wuvorbis/WuVorbisMainUnit.cpp 等参照
	// }
	// TVPGetcommandLine の実行は現バージョンでは高価なのでできれば
	// この関数の呼び出しの結果はキャッシュした方がよい。

	// 吉里吉里設定は、プラグインを「吉里吉里本体と同じフォルダ」と
	// 「吉里吉里本体のと同じフォルダにある plugin というフォルダ」から
	// しか検索しない。従って設定可能なオプションを含むプラグインは
	// Releaser などでアーカイブ中に含めてはならない(もともとReleaserで
	// プラグインを中に含めることは推奨されていない)。

	// この GetOptionDesc 関数内では tp_stub に記述されているような
	// 各吉里吉里本体内の関数は利用できない。
}
//---------------------------------------------------------------------------
/*
	V2Link と V2Unlink は DLL からエクスポートされている必要がある。
	従って、.def ファイルを作成し

EXPORTS
	V2Link
	V2Unlink

	と記述してプロジェクトに追加する。

	GetOptionsDesc を使う場合はそれも追加する。
*/
//---------------------------------------------------------------------------

